#pragma once
#include "common.hpp"
#include <pthread.h>
#include <semaphore.h>
#include <vector>
template<class T>
class RingQueue
{
public:
    RingQueue(int n) : _v(n), _p_idx(0), _c_idx(0)
    {
        pthread_mutex_init(&_p_mutex, nullptr);
        pthread_mutex_init(&_c_mutex, nullptr);
        sem_init(&_data_sem, 0, 0);
        sem_init(&_space_sem, 0, n);
    }

    void Push(const T& data)
    {
        sem_wait(&_space_sem);
        {
            LockGuard lock(&_p_mutex);
            _v[_p_idx++] = data;
            _p_idx %= _v.size();
        }
        sem_post(&_data_sem);
    }

    void Push(T& data)
    {
        sem_wait(&_space_sem);
        {
            LockGuard lock(&_p_mutex);
            _v[_p_idx++] = data;
            _p_idx %= _v.size();
        }
        sem_post(&_data_sem);
    }

    void Pop(T* out)
    {
        sem_wait(&_data_sem);
        {
            LockGuard lock(&_c_mutex);
            *out = _v[_c_idx++];
            _c_idx %= _v.size();
        }
        sem_post(&_space_sem);
    }

    ~RingQueue()
    {
        pthread_mutex_destroy(&_p_mutex);
        pthread_mutex_destroy(&_c_mutex);
        sem_destroy(&_data_sem);
        sem_destroy(&_space_sem);
    }
private:
    std::vector<T> _v;
    pthread_mutex_t _p_mutex;
    pthread_mutex_t _c_mutex;
    sem_t _data_sem;
    sem_t _space_sem;
    int _p_idx = 0;
    int _c_idx = 0;
};